polish: improve UI feel across the board#107
Conversation
…dates Bumps the npm_and_yarn group with 2 updates in the / directory: [next](https://github.com/vercel/next.js) and [flatted](https://github.com/WebReflection/flatted). Updates `next` from 16.1.6 to 16.1.7 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](vercel/next.js@v16.1.6...v16.1.7) Updates `flatted` from 3.4.1 to 3.4.2 - [Commits](WebReflection/flatted@v3.4.1...v3.4.2) --- updated-dependencies: - dependency-name: next dependency-version: 16.1.7 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: flatted dependency-version: 3.4.2 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com>
- Replace all transition-all with specific transition properties to avoid animating layout-triggering properties - Add active:scale-[0.96] to buttons for tactile press feedback - Add tabular-nums to all dynamically updating numbers (~20 files) to prevent layout shift on value changes - Add text-wrap: balance on headings, text-wrap: pretty on body text across Card, Dialog, PageHeader, EmptyState, auth layout, not-found - Fix concentric border radius on flow nodes (overflow-hidden + remove inner rounded-t-lg) - Add layered box-shadow to Card component for natural depth - Add subtle outline to AvatarImage for edge definition - Add optional pulse prop to StatusDot for liveness indication - Expand hit areas on small icon buttons (dashboard tab actions, update banner dismiss) with pseudo-element technique - Add background-color/color to sidebar menu button transitions for smoother hover states
Greptile SummaryA thorough UI polish pass across 37 components applying consistent improvements: specific
Confidence Score: 4/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[StatusDot pulse=true] --> B{variant?}
B -->|healthy/degraded/error| C["Apply pulseStyles class\n(shadow-[0_0_0_0_var(--status-*)] + animate-[status-pulse])"]
B -->|neutral/info| D[No pulse — empty string]
C --> E["CSS animation runs\n@keyframes status-pulse"]
E --> F["0%/100%: box-shadow 0 0 0 0 currentColor\n50%: box-shadow 0 0 4px transparent"]
F --> G{What is currentColor?}
G -->|Expected| H["var(--status-healthy/degraded/error)\n✅ Status-colored ring"]
G -->|Actual| I["Inherited text color (foreground)\n❌ Wrong color ring"]
I --> J["shadow-[0_0_0_0_var(--status-*)] Tailwind\nclass is overridden by animation\n— dead code"]
style I fill:#ef4444,color:#fff
style H fill:#22c55e,color:#fff
style J fill:#f97316,color:#fff
Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/components/ui/status-dot.tsx
Line: 13-19
Comment:
**Pulse ring renders in the wrong color**
The `@keyframes status-pulse` animation in `globals.css` uses `currentColor` for the box-shadow:
```css
0%, 100% { box-shadow: 0 0 0 0 currentColor; }
50% { box-shadow: 0 0 0 4px transparent; }
```
CSS animations override regular property declarations, so this keyframe completely replaces the `shadow-[0_0_0_0_var(--status-healthy)]` Tailwind utility on the element. `currentColor` resolves to the CSS `color` property — but `StatusDot` only sets `background-color` (via `bg-status-*`); it never sets `color`. As a result, the expanding ring will be the inherited *text* color (e.g. neutral gray), not the status color (green / amber / red).
The `shadow-[0_0_0_0_var(--status-*)]` entries in `pulseStyles` are dead code while the animation is running.
**Fix:** add `color: var(--status-*)` to the element so that `currentColor` resolves to the right hue:
```suggestion
const pulseStyles: Record<StatusVariant, string> = {
healthy: "text-[var(--status-healthy)] animate-[status-pulse_2s_ease-in-out_infinite]",
degraded: "text-[var(--status-degraded)] animate-[status-pulse_2s_ease-in-out_infinite]",
error: "text-[var(--status-error)] animate-[status-pulse_1.5s_ease-in-out_infinite]",
neutral: "",
info: "",
};
```
Setting `color` via `text-[var(--status-*)]` won't change the dot's visible colour (that comes from `background-color`), but it makes `currentColor` resolve to the correct value inside the keyframe.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "polish: improve UI feel across the board" | Re-trigger Greptile |
| const pulseStyles: Record<StatusVariant, string> = { | ||
| healthy: "shadow-[0_0_0_0_var(--status-healthy)] animate-[status-pulse_2s_ease-in-out_infinite]", | ||
| degraded: "shadow-[0_0_0_0_var(--status-degraded)] animate-[status-pulse_2s_ease-in-out_infinite]", | ||
| error: "shadow-[0_0_0_0_var(--status-error)] animate-[status-pulse_1.5s_ease-in-out_infinite]", | ||
| neutral: "", | ||
| info: "", | ||
| }; |
There was a problem hiding this comment.
Pulse ring renders in the wrong color
The @keyframes status-pulse animation in globals.css uses currentColor for the box-shadow:
0%, 100% { box-shadow: 0 0 0 0 currentColor; }
50% { box-shadow: 0 0 0 4px transparent; }CSS animations override regular property declarations, so this keyframe completely replaces the shadow-[0_0_0_0_var(--status-healthy)] Tailwind utility on the element. currentColor resolves to the CSS color property — but StatusDot only sets background-color (via bg-status-*); it never sets color. As a result, the expanding ring will be the inherited text color (e.g. neutral gray), not the status color (green / amber / red).
The shadow-[0_0_0_0_var(--status-*)] entries in pulseStyles are dead code while the animation is running.
Fix: add color: var(--status-*) to the element so that currentColor resolves to the right hue:
| const pulseStyles: Record<StatusVariant, string> = { | |
| healthy: "shadow-[0_0_0_0_var(--status-healthy)] animate-[status-pulse_2s_ease-in-out_infinite]", | |
| degraded: "shadow-[0_0_0_0_var(--status-degraded)] animate-[status-pulse_2s_ease-in-out_infinite]", | |
| error: "shadow-[0_0_0_0_var(--status-error)] animate-[status-pulse_1.5s_ease-in-out_infinite]", | |
| neutral: "", | |
| info: "", | |
| }; | |
| const pulseStyles: Record<StatusVariant, string> = { | |
| healthy: "text-[var(--status-healthy)] animate-[status-pulse_2s_ease-in-out_infinite]", | |
| degraded: "text-[var(--status-degraded)] animate-[status-pulse_2s_ease-in-out_infinite]", | |
| error: "text-[var(--status-error)] animate-[status-pulse_1.5s_ease-in-out_infinite]", | |
| neutral: "", | |
| info: "", | |
| }; |
Setting color via text-[var(--status-*)] won't change the dot's visible colour (that comes from background-color), but it makes currentColor resolve to the correct value inside the keyframe.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/components/ui/status-dot.tsx
Line: 13-19
Comment:
**Pulse ring renders in the wrong color**
The `@keyframes status-pulse` animation in `globals.css` uses `currentColor` for the box-shadow:
```css
0%, 100% { box-shadow: 0 0 0 0 currentColor; }
50% { box-shadow: 0 0 0 4px transparent; }
```
CSS animations override regular property declarations, so this keyframe completely replaces the `shadow-[0_0_0_0_var(--status-healthy)]` Tailwind utility on the element. `currentColor` resolves to the CSS `color` property — but `StatusDot` only sets `background-color` (via `bg-status-*`); it never sets `color`. As a result, the expanding ring will be the inherited *text* color (e.g. neutral gray), not the status color (green / amber / red).
The `shadow-[0_0_0_0_var(--status-*)]` entries in `pulseStyles` are dead code while the animation is running.
**Fix:** add `color: var(--status-*)` to the element so that `currentColor` resolves to the right hue:
```suggestion
const pulseStyles: Record<StatusVariant, string> = {
healthy: "text-[var(--status-healthy)] animate-[status-pulse_2s_ease-in-out_infinite]",
degraded: "text-[var(--status-degraded)] animate-[status-pulse_2s_ease-in-out_infinite]",
error: "text-[var(--status-error)] animate-[status-pulse_1.5s_ease-in-out_infinite]",
neutral: "",
info: "",
};
```
Setting `color` via `text-[var(--status-*)]` won't change the dot's visible colour (that comes from `background-color`), but it makes `currentColor` resolve to the correct value inside the keyframe.
How can I resolve this? If you propose a fix, please make it concise.
Systematic UI polish pass applying interface-feel-better principles across 37 files. No functional changes, no new dependencies.
Changes
Transitions
transition-allwith specific properties (button, switch, tabs, sidebar, theme toggle, analytics progress bars) to avoid animating layout-triggering propertiesTactile Feedback
active:scale-[0.96]press feedback to all Button variants (except link)Tabular Numbers
tabular-numsto every dynamically updating number (~20 files) — dashboard KPIs, summary cards, uptime percentages, flow node metrics, log timestamps, chart tooltips, alert values, backup sizesTypography
text-wrap: balanceon headings (CardTitle, DialogTitle, PageHeader, EmptyState, auth layout, not-found)text-wrap: prettyon descriptions (CardDescription, DialogDescription, PageHeader, EmptyState, query-error, auth layout, not-found)Surfaces
overflow-hiddenon parent, remove innerrounded-t-lgbox-shadowto Card for natural depth (light + dark variants)outlineto AvatarImage for edge definition against matching backgroundsInteractions
background-color, colorto sidebar menu button transitions for smoother hoverLiveness
pulseprop to StatusDot with CSS keyframe animation (respectsprefers-reduced-motion)Verification
tsc --noEmit✓eslint src/✓vitest run— 105/105 tests pass ✓